package com.hhw.mocklocationsample;

import android.annotation.SuppressLint;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Build;
import android.os.SystemClock;

import java.lang.reflect.Method;

public class LocationMockUtils {
	
	private Context mContext;
	private LocationManager mLocationManager = null;
	private final String MockProviderName = LocationManager.GPS_PROVIDER;
	private boolean isOpenMock = true;

	public void init(Context context) {
		this.mContext = context;
		mLocationManager = (LocationManager) context
				.getSystemService(Context.LOCATION_SERVICE);

		writeMockLocation(mContext.getPackageName());
	}

	private void writeMockLocation(String mockPackageName) {
		try {
			AppOpsManager appsOpsManager = (AppOpsManager) mContext
					.getSystemService(Context.APP_OPS_SERVICE);

			@SuppressLint("WrongConstant") final ApplicationInfo ai = mContext.getPackageManager()
					.getApplicationInfo(mockPackageName,
							PackageManager.GET_DISABLED_COMPONENTS);
			Class c = Class.forName("android.app.AppOpsManager");

			Class[] args = new Class[4];
			args[0] = int.class;
			args[1] = int.class;
			args[2] = String.class;
			args[3] = int.class;

			Method setMode = c.getMethod("setMode", args);
			Object[] params = new Object[4];
			params[0] = Integer.valueOf(58);
			params[1] = Integer.valueOf(ai.uid);
			params[2] = new String(mockPackageName);
			params[3] = Integer.valueOf(AppOpsManager.MODE_ALLOWED);
			setMode.invoke(appsOpsManager, params);

		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (Exception e1) {
			e1.printStackTrace();
		}

	}


	@SuppressLint("WrongConstant")
	public void enableLocationMock(boolean isEnable) {
		if (isEnable){
			try {
				mLocationManager.addTestProvider(MockProviderName,
						"requiresNetwork" == "", "requiresSatellite" == "",
						"requiresCell" == "", "hasMonetaryCost" == "",
						"supportsAltitude" == "supportsAltitude",
						"supportsSpeed" == "supportsSpeed",
						"supportsBearing" == "supportsBearing",
						Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
				mLocationManager.setTestProviderEnabled(MockProviderName, true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}else{
			try {
				mLocationManager.clearTestProviderEnabled(MockProviderName);
				mLocationManager.removeTestProvider(MockProviderName);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void mockLocation(boolean location, double latitude,
			double longitude, float speed, float bearing, byte starCount) {

		mockLocation(location
				,latitude / 1000000.0
				,longitude / 1000000.0
				,speed
				,bearing
				,starCount
				,0
		);
	}

	private void mockLocation(boolean location, double latitude,
							  double longitude, float speed, float bearing, byte starCount,double altitude) {

		Location loc = new Location(MockProviderName);// 这里是模拟的gps位置信息，当然也可以设置network位置信息了。
		loc.setTime(System.currentTimeMillis());
		loc.setLatitude(latitude);
		loc.setLongitude(longitude);
		loc.setSpeed((float) (speed / 3.6));
		loc.setBearing(bearing);
		loc.setAltitude(altitude);

		if (location) {
			loc.setAccuracy(10);
		} else {
			loc.setAccuracy(100);
		}

		loc.setAltitude(starCount);
		loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			loc.setBearingAccuracyDegrees(0.1f);
			loc.setVerticalAccuracyMeters(0.1f);
			loc.setSpeedAccuracyMetersPerSecond(0.01f);
		}
		if (isOpenMock&& mLocationManager != null) {
			try {
				mLocationManager.setTestProviderLocation(MockProviderName, loc);
				mLocationManager.setTestProviderStatus(MockProviderName,
						LocationProvider.AVAILABLE, null,
						System.currentTimeMillis());
			}catch (SecurityException e) {
				isOpenMock = false;
				e.printStackTrace();
			}catch (Exception e) {
				e.printStackTrace();
			}
			
		}
	}
}
